package binpack

import (
	"fmt"
	"gitee.com/ymofen/gobase"
	"io"
)

const (
	// key  max:255
	// data 0xFFFF max:65535
	// 包协议, minL: 8=1+2+2+3, bodystart:5
	// pre[1]+[0x80]keyl1+[0x80]keyl2 +dataL(LE:2)+data[key+data]+crc24[3]

	BinPackCrc32_MinL              = 9
	BinPackCrc32_DataStartIdx      = 5
	BinPackCrc32_PRE0         byte = 0xFA
)

func WriteBinPackCrc32(w io.Writer, msgid uint16, data []byte) (err error) {
	w.Write([]byte{BinPackCrc32_PRE0})

	l1 := msgid
	l2 := len(data)
	if l1 > 0xFFF {
		return fmt.Errorf("msgid must less than %d", 0xFFF)
	}

	if l2 > 0xFFFF {
		return fmt.Errorf("data size must less than %d", 0xFFFF)
	}

	crc := uint32(0)
	l1 = 0xA000 | l1
	buf := []byte{byte(l1 >> 8), byte(l1), byte(l2 >> 8), byte(l2)}

	w.Write(buf)
	crc = gobase.CRC32Continue(crc, buf...)

	w.Write(data)
	crc = gobase.CRC32Continue(crc, data...)

	w.Write([]byte{byte(crc >> 24), byte(crc >> 16), byte(crc >> 8), byte(crc)})
	return nil
}

type BinPackCrc32Decode struct {
	decodestep int8

	msgid  uint16
	datal  int
	msgl   int
	cachel int
	cache  gobase.BytesBuilder
}

func (this *BinPackCrc32Decode) ExtractMsg() (msgid uint16, data []byte) {
	return this.msgid, this.cache.Buff(BinPackCrc32_DataStartIdx, this.datal)
}

// 0: need more
// 1: ok
// -1: crc
// -2: errdata
func (this *BinPackCrc32Decode) InputByte(v byte) int {
	if this.decodestep < 0 {
		this.cache.Reset()
		this.msgid = 0
		this.cachel = 0
		this.datal = 0
		this.decodestep = 1
	}

	this.cache.WriteByte(v)
	this.cachel++
	if this.cachel > BinPackCrc32_DataStartIdx {
		if this.cachel < this.msgl {
			return 0
		} else {
			crc0 := this.cache.Uint32_BE(this.cachel - 4)
			crc1 := gobase.CRC32Buf(this.cache.BuffEx(1, this.cachel-4))
			this.decodestep = -1
			if crc1 != crc0 {
				return -1
			}
			return 1
		}
	}
	if this.cachel == 1 {
		if v != BinPackCrc32_PRE0 {
			this.decodestep = -1
			return -2
		}
	} else if this.cachel == 3 {
		this.msgid = this.cache.Uint16_BE(1) & 0x0FFF
	} else if this.cachel == 5 {
		this.datal = int(this.cache.Uint16_BE(3))
		this.msgl = this.datal + BinPackCrc32_MinL
	}

	return 0
}
